/*
 * UploadVideoService.java
 * 
 * Copyright©2012-2013 JMAS corporation. All rights reserved.
 */
package jp.co.jmas.jmasvideo.task;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Date;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.cookie.Cookie;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.json.JSONException;

import jp.co.jmas.jmasvideo.AppInfo;
import jp.co.jmas.jmasvideo.R;
import jp.co.jmas.jmasvideo.Util;
import jp.co.jmas.jmasvideo.dao.ClassInfo;
import jp.co.jmas.jmasvideo.dao.Student;
import jp.co.jmas.jmasvideo.dao.Video;
import jp.co.jmas.jmasvideo.task.InputStreamEntityWithProgress.ProgressListener;
import android.accounts.AccountManager;
import android.app.IntentService;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import android.widget.Toast;

public class UploadVideoService extends IntentService implements ProgressListener {

	/** Intent key (type:Video) */
	public static final String KEY_VIDEO = "VIDEO";

	/** Intent key (type:Video) */
	public static final String KEY_STUDENT = "STUDENT";

	/** Intent key (type:ClassInfo) */
	public static final String KEY_CLASS = "KEY_CLASS";

	/** Intent key (type:String) */
	public static final String KEY_AUTHTOKEN_APPENGINE = "AUTH_APPENGINE";

	/** Intent key (type:String) */
	public static final String KEY_AUTHTOKEN_CLOUDSTORAGE = "AUTH_CLOUDSTORAGE";

	/** Service name */
	private static final String SERVICE_NAME = "UploadVideoService";

	/** Video */
	private Video video;

	/** Student */
	private Student student;

	/** classInfo */
	private ClassInfo classInfo;

	/** Authentication token of AppEngine */
	private String authTokenAppEngine;

	/** Authentication token of CloudStorage */
	private String authTokenCloudStorage;

	private NotificationManager mNotifyManager;

	private NotificationCompat.Builder mBuilder;

	private static int ID_NOTIFICATION = 0;

	public UploadVideoService() {
		this(SERVICE_NAME);
	}

	public UploadVideoService(String name) {
		super(name);
	}

	/* (non-Javadoc)
	 * @see android.app.IntentService#onHandleIntent(android.content.Intent)
	 */
	@Override
	protected void onHandleIntent(Intent intent) {
		// 非同期処理を行うメソッド。タスクはonHandleIntentメソッド内で実行する
		Log.d("IntentService", "onHandleIntent Start");

		Bundle extras = intent.getExtras();
		video = new Video();
		if (extras != null) {
			// TODO インテントにvideo, student, classInfoを渡す方法は要改善。
			video     = (Video)extras.getSerializable(KEY_VIDEO);
			student   = (Student)extras.getSerializable(KEY_STUDENT);
			classInfo = (ClassInfo)extras.getSerializable(KEY_CLASS);
			video.setStudent(student);
			student.setClassInfo(classInfo);
			authTokenAppEngine    = extras.getString(KEY_AUTHTOKEN_APPENGINE);
			authTokenCloudStorage = extras.getString(KEY_AUTHTOKEN_CLOUDSTORAGE);
		} else {
			Log.d("UploadVideoService", "InvalidIntent");
			return;
		}

		this.mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
		this.mBuilder = new NotificationCompat.Builder(this);

		String name = Util.getStudentLabel(student);
		mBuilder.setContentTitle(name + "\nの動画を送信中...");
		mBuilder.setSmallIcon(R.drawable.ic_launcher);
		mBuilder.setProgress(100, 0, false);
		mBuilder.setTicker(name + "の動画を送信中...");
		mNotifyManager.notify(ID_NOTIFICATION, mBuilder.build());

		try {
			// 
			uploadFile( 0, 80, Util.getVideoPath(video), "video/mpeg");
			uploadFile(80, 90, Util.getThumbnailPath(video), "image/jpeg");
			postVideoInfo(90, 98);

			// 99%で500ms止める！
			progressUpdate(99);
			Thread.sleep(500);

			Toast.makeText(getApplicationContext(), "Upload completed.", Toast.LENGTH_SHORT).show();

			// When the loop is finished, updates the notification
			mBuilder.setContentText("動画の送信が完了しました");

			// Removes the progress bar
			mBuilder.setProgress(0,0, false);
			mNotifyManager.notify(ID_NOTIFICATION, mBuilder.build());

		} catch (UploadException e) {
			Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_SHORT).show();
			if (e.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
				final AccountManager accountManager = AccountManager.get(this);
				accountManager.invalidateAuthToken("com.google", authTokenCloudStorage);
			}

			mBuilder.setContentText("動画の送信に失敗しました。");
			mNotifyManager.notify(ID_NOTIFICATION, mBuilder.build());

		} catch (InterruptedException e) {
			Toast.makeText(getApplicationContext(), "Upload canceled.", Toast.LENGTH_SHORT).show();

			mBuilder.setContentText("動画の送信をキャンセルしました。");
			mNotifyManager.notify(ID_NOTIFICATION, mBuilder.build());

		}

	}

	/**
	 * @param startProgress
	 * @param endProgress
	 * @param filePath
	 * @throws UploadException
	 * @return
	 */
	public void uploadFile(int startProgress, int endProgress, String filePath, String mimeType) throws UploadException {
		HttpClient client = new DefaultHttpClient();
		File file = new File(filePath);
		String url = String.format("https://commondatastorage.googleapis.com/%s/%s", AppInfo.BUCKET, file.getName());
		HttpPut httpPut = new HttpPut(url);
		httpPut.setHeader("Host", String.format("commondatastorage.googleapis.com", AppInfo.BUCKET));
		httpPut.setHeader("Date", new Date().toGMTString()); // format
		httpPut.setHeader("Authorization", String.format("OAuth %s", authTokenCloudStorage));

		HttpResponse response;
		try {
			InputStreamEntity entity = new InputStreamEntityWithProgress(new FileInputStream(file), file.length(), startProgress, endProgress, this);
			entity.setContentType(mimeType);
			httpPut.setEntity(entity);
			response = client.execute(httpPut);
			HttpEntity resEntity = response.getEntity();

			int statusCode = response.getStatusLine().getStatusCode();
			String content = null;
			if (resEntity != null) {
				content = EntityUtils.toString(resEntity, "UTF-8");
			}
			if (resEntity != null) {
				resEntity.consumeContent();
			}

			if (statusCode >= HttpStatus.SC_BAD_REQUEST) {
				throw new UploadException(statusCode, content);
			}

		} catch (ClientProtocolException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			client.getConnectionManager().shutdown();
		}
	}

	public void postVideoInfo(int startProgress, int endProgress) throws UploadException{
		HttpClient client = null;
		{
			try {
				client = login(authTokenAppEngine);
				String url = AppInfo.getServer() + "/rest/videos";
				String json = Util.convertVideoToJson(video).toString(4);
				byte[] data = json.getBytes("UTF-8");
				InputStreamEntity entity = new InputStreamEntityWithProgress(new ByteArrayInputStream(data), data.length, startProgress, endProgress, this);
				entity.setContentType("application/json");
				HttpPost httpPost = new HttpPost(url);
				httpPost.setEntity(entity);

				HttpResponse response = client.execute(httpPost);
				int statusCode = response.getStatusLine().getStatusCode();
				HttpEntity resEntity = response.getEntity();
				String content = null;
				if (resEntity != null) {
					content = EntityUtils.toString(resEntity, "UTF-8");
				}
				if (resEntity != null) {
					resEntity.consumeContent();
				}
				if (statusCode >= HttpStatus.SC_BAD_REQUEST) {
					throw new UploadException(statusCode, content);
				}

			} catch (ClientProtocolException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (JSONException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} finally {
				if (client != null) {
					client.getConnectionManager().shutdown();
				}
			}
		}
	}

	private static HttpClient login(String token) {
		DefaultHttpClient client = new DefaultHttpClient();
		try {
			// Don't follow redirects
			client.getParams().setBooleanParameter(ClientPNames.HANDLE_REDIRECTS, false);
	
			HttpGet http_get = new HttpGet(AppInfo.getServer() + "/_ah/login?continue=http://localhost/&auth=" + token);
			HttpResponse response;
			response = client.execute(http_get);
			if (response.getStatusLine().getStatusCode() == 302) {
				// Success (Response should be a redirect)
				for (Cookie cookie : client.getCookieStore().getCookies()) {
					if (cookie.getName().equals("ACSID") || cookie.getName().equals("SACSID")) {
						return client;
					}
				}
			}
		} catch (ClientProtocolException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			client.getParams().setBooleanParameter(ClientPNames.HANDLE_REDIRECTS, true);
		}
		return null;
	}

	@Override
	public void progressUpdate(int progress) {
		mBuilder.setProgress(100, progress, false);

		// Displays the progress bar for the first time.
		mNotifyManager.notify(ID_NOTIFICATION, mBuilder.build());
	}

	private static class UploadException extends Exception {
		/** Serial UID */
		private static final long serialVersionUID = 1L;

		public final int statusCode;

		public UploadException(int statusCode, String message) {
			super(message);
			this.statusCode = statusCode;
		}

		public int getStatusCode() {
			return statusCode;
		}
	}
}
